//------------------------------------------------------------------------------
//  db/dbserver.cc
//  (C) 2006 Radon Labs GmbH
//------------------------------------------------------------------------------
#include "stdneb.h"
#include "addons/db/dbserver.h"
#include "addons/db/factory.h"
#include "io/ioserver.h"

namespace Db
{
ImplementClass(Db::DbServer, 'DBSV', Core::RefCounted);
ImplementSingleton(Db::DbServer);

//------------------------------------------------------------------------------
/**
*/
DbServer::DbServer() :
    isOpen(false),
    accessMode(Database::ReadWriteExisting)
{
    ConstructSingleton;
    this->dbFactory = Db::Sqlite3Factory::Create();
}

//------------------------------------------------------------------------------
/**
*/
DbServer::~DbServer()
{
    n_assert(!this->IsOpen());
    this->dbFactory = 0;
    DestructSingleton;
}

//------------------------------------------------------------------------------
/**
    Directly open a world database. The database file must exist!
*/
bool
DbServer::Open(const Util::String& dbURI)
{
    n_assert(!this->isOpen);
    n_assert(dbURI.IsValid());
    n_assert(!this->db.isvalid());
    this->db = Factory::Instance()->CreateDatabase();
    this->db->SetURI(dbURI);
    this->db->SetAccessMode(accessMode);
    this->db->SetIgnoreUnknownColumns(true);
    if (this->db->Open())
    {
        this->isOpen = true;
        return true;
    }
    else
    {
        this->db = 0;
        return false;
    }
}

//------------------------------------------------------------------------------
/**
    Close the current world database.
*/
void
DbServer::Close()
{
    n_assert(this->isOpen);
    n_assert(this->db.isvalid());
    n_assert(this->db->IsOpen());
    this->db->Close();
    n_assert(this->db->GetRefCount() == 1);
    this->db = 0;
    this->isOpen = false;
}

//------------------------------------------------------------------------------
/**
    This deletes the current game state database.
*/
void
DbServer::DeleteCurrentGame(const Util::String& profileURI)
{
    // close database if open
    if (this->IsOpen())
    {
        this->Close();
    }

    // delete current database
    IO::IoServer* ioServer = IO::IoServer::Instance();
    if (ioServer->FileExists(profileURI))
    {
        bool dbDeleted = ioServer->DeleteFile(profileURI);
        n_assert(dbDeleted);
    }
}

//------------------------------------------------------------------------------
/**
    This opens the database in New Game mode: an original database will be
    copied into the user's profile directory into a Current Game State
    database and opened.
*/
bool
DbServer::OpenNewGame(const Util::String& profileURI, const Util::String& dbURI)
{
    n_assert(profileURI.IsValid());

    // make sure we're not open
    if (this->IsOpen())
    {
        this->Close();
    }
    
    // make sure the target directory exists
    IO::IoServer* ioServer = IO::IoServer::Instance();
    ioServer->CreateDirectory(profileURI);
    
    // delete current database
    if (ioServer->FileExists(dbURI))
    {
        bool dbDeleted = ioServer->DeleteFile(dbURI);
        n_assert(dbDeleted);
    }
    bool dbCopied = ioServer->CopyFile("export:db/world.db4", dbURI);
    n_assert(dbCopied);

    // open the copied database file
    bool dbOpened = this->Open(dbURI);
    return dbOpened;
}

//------------------------------------------------------------------------------
/**
    This opens the database in Continue Game mode: the current game database
    in the user profile directory will simply be opened.
*/
bool
DbServer::OpenContinueGame(const Util::String& profileURI)
{
    n_assert(profileURI.IsValid());

    // make sure we're not open
    if (this->IsOpen())
    {
        this->Close();
    }
    
    // open the current game database
    bool dbOpened = this->Open(profileURI);
    return dbOpened;
}

//------------------------------------------------------------------------------
/**
    Return true if a current game database exists.
*/
bool
DbServer::CurrentGameExists(const Util::String& profileURI) const
{
    n_assert(profileURI.IsValid());
    return IO::IoServer::Instance()->FileExists(profileURI);
}

//------------------------------------------------------------------------------
/**
    This opens the database in Load Game mode. This will overwrite the
    current game database with a save game database and open this as
    usual.
*/
bool
DbServer::OpenLoadGame(const Util::String& profileURI, const Util::String& dbURI, const Util::String& saveGameURI)
{
    n_assert(profileURI.IsValid());
    
    // make sure we're not open
    if (this->IsOpen())
    {
        this->Close();
    }
    
    // make sure the target directory exists
    IO::IoServer* ioServer = IO::IoServer::Instance();
    ioServer->CreateDirectory(profileURI);

    // check if save game exists
    if (!ioServer->FileExists(saveGameURI))
    {
        return false;
    }
    
    // delete current database
    if (ioServer->FileExists(dbURI))
    {
        bool dbDeleted = ioServer->DeleteFile(dbURI);
        n_assert(dbDeleted);
    }
    bool dbCopied = ioServer->CopyFile(saveGameURI, dbURI);
    n_assert(dbCopied);

    // open the copied database file
    bool dbOpened = this->Open(dbURI);
    return dbOpened;
}

//------------------------------------------------------------------------------
/**
    This creates a new save game by making a copy of the current world
    database into the savegame directory. If a savegame of that exists,
    it will be overwritten.
*/
bool
DbServer::CreateSaveGame(const Util::String& profileURI, const Util::String& dbURI, const Util::String& saveGameURI)
{
    // make sure the target directory exists
    IO::IoServer* ioServer = IO::IoServer::Instance();
    ioServer->CreateDirectory(profileURI);

    // check if current world database exists
    if (!ioServer->FileExists(dbURI))
    {
        return false;
    }

    // delete save game file if already exists
    if (ioServer->FileExists(saveGameURI))
    {
        bool saveGameDeleted = ioServer->DeleteFile(saveGameURI);
        n_assert(saveGameDeleted);
    }
    else
    {
        Util::String saveGamePath = saveGameURI.ExtractDirName();        
        ioServer->CreateDirectory(saveGamePath);
    }
    bool saveGameCopied = ioServer->CopyFile(dbURI, saveGameURI);
    n_assert(saveGameCopied);
    return true;
}

} // namespace Db